/**
 * fulihui.com Inc.
 * Copyright (c) 2015-2016 All Rights Reserved.
 */
package com.fulihui.integral.core.service;


import com.fulihui.integral.core.api.ISUserBehaviorService;
import com.fulihui.integral.core.common.AppConstConfig;
import com.fulihui.integral.core.common.ISBaseErrors;
import com.fulihui.integral.core.common.MACID;
import com.fulihui.integral.core.common.constants.ExtParamKeys;
import com.fulihui.integral.core.dal.dataobj.ISAccountDetail;
import com.fulihui.integral.core.dal.dataobj.ISAccountDetailExample;
import com.fulihui.integral.core.dal.mapper.ISAccountDetailMapper;
import com.fulihui.integral.core.dal.repository.ISBaseQueryRepository;
import com.fulihui.integral.core.dal.repository.ISUserBehaviorRepository;
import com.fulihui.integral.core.dal.repository.result.TransactionResult;
import com.fulihui.integral.core.dto.ISAccountInfoDTO;
import com.fulihui.integral.core.enums.DuibaType;
import com.fulihui.integral.core.enums.ISAccountType;
import com.fulihui.integral.core.enums.ISChannelType;
import com.fulihui.integral.core.enums.ISDoType;
import com.fulihui.integral.core.handler.IntegralMsgBusinessSelector;
import com.fulihui.integral.core.request.ISAccountInfoQueryReq;
import com.fulihui.integral.core.request.ISUserBehaviorReq;
import com.fulihui.integral.core.request.RewardPointsReq;
import com.fulihui.integral.core.request.duiba.*;
import com.fulihui.integral.core.result.ISUserBehaviorResult;
import com.fulihui.integral.core.result.duiba.DuibaLoginResult;
import com.fulihui.integral.core.result.duiba.DuibaResult;
import com.fulihui.integral.core.strategy.RewardContext;
import com.fulihui.integral.core.strategy.StrategyParam;
import com.fulihui.integral.core.strategy.StrategyResult;
import com.fulihui.integral.core.util.ClassFieldsUtil;
import com.fulihui.integral.core.util.RedisUtil;
import com.fulihui.integral.core.util.SignTool;
import com.fulihui.mscore.api.MessageService;
import com.fulihui.mscore.enums.MsgTypeEnum;
import com.fulihui.mscore.request.MsgCreateRequest;
import org.near.servicesupport.request.RequestBuilder;
import org.near.servicesupport.result.ResultBuilder;
import org.near.servicesupport.util.ConditionValidator;
import org.near.toolkit.common.DateUtils;
import org.near.toolkit.common.RandomCharset;
import org.near.toolkit.common.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;
import javax.inject.Inject;
import java.util.*;

/**
 * 用户积分账户操作
 *
 * @author yunfeng.li
 * @version $Id: v 0.1 2017年04月13日 11:52 yunfeng.li Exp $
 */
@Service("iSUserBehaviorService") public class ISUserBehaviorServiceImpl
        implements ISUserBehaviorService {

    @Resource private ISUserBehaviorRepository    iSUserBehaviorRepository;
    @Resource private RewardContext               rewardContext;
    @Resource         IntegralMsgBusinessSelector integralMsgBusinessSelector;
    @Resource         ThreadPoolTaskExecutor      threadPool;
    @Resource         MessageService              messageService;

    @Resource private ISBaseQueryRepository iSBaseQueryRepository;

    @Autowired private AppConstConfig appConstConfig;

    @Resource private ISAccountDetailMapper iSAccountDetailMapper;

    private Logger logger = LoggerFactory.getLogger(ISUserBehaviorServiceImpl.class);

    @Autowired private JedisPool jedisPool;

    public static final String LOCK_USER_KEY = "DUIBA:USER:";


    @Autowired private UserInfoService userInfoService;

    @Autowired private SignSuccessService signSuccessService;

    /**
     * 加积分
     *
     * @param req
     * @return 操作结果
     */
    @Override public ISUserBehaviorResult doAdd(ISUserBehaviorReq req) {
        ConditionValidator.validate(req);
        TransactionResult result = iSUserBehaviorRepository
                .addOrSubAmount(req, ISDoType.ADD.getCode());
        if (result.isSuccess()) {
            threadPool.execute(() -> {
                MsgCreateRequest msgCreateRequest = new MsgCreateRequest();
                msgCreateRequest.setTarget(req.getUserId());
                msgCreateRequest.setMsgType(MsgTypeEnum.MESSAGE_JIFEN.getCode());
                messageService.createMsg(RequestBuilder.buildT(msgCreateRequest));
            });
            return ResultBuilder.succ(ISUserBehaviorResult.class);
        }
        return ResultBuilder
                .fail(ISUserBehaviorResult.class, result.getErrorCode(), result.getErrorMsg());
    }

    /**
     * 减积分
     *
     * @param req
     * @return 操作结果
     */
    @Override public ISUserBehaviorResult doSub(ISUserBehaviorReq req) {
        ConditionValidator.validate(req);
        TransactionResult result = iSUserBehaviorRepository
                .addOrSubAmount(req, ISDoType.SUB.getCode());
        if (result.isSuccess()) {
            return ResultBuilder.succ(ISUserBehaviorResult.class);
        }
        return ResultBuilder
                .fail(ISUserBehaviorResult.class, result.getErrorCode(), result.getErrorMsg());
    }

    /**
     * 积分奖励
     *
     * @param req
     * @return
     */
    @Override public ISUserBehaviorResult rewardPoints(RewardPointsReq req) {
        ConditionValidator.validate(req);
        Map<String, String> extParam = req.getExtParam();
        String outTradeNo = req.getOutTradeNo();
        TransactionResult result = null;
        //执行积分奖励策略
        StrategyParam param = new StrategyParam();
        param.setChannel(req.getChannel());
        param.setUserId(req.getUserId());
        param.setExtParam(extParam);
        StrategyResult strategyResult = rewardContext.calculate(param);
        if (strategyResult != null && strategyResult.getAmount() > 0) {
            ISUserBehaviorReq request = new ISUserBehaviorReq();
            request.setAmount(strategyResult.getAmount());
            request.setIsChannelType(StringUtil.isBlank(strategyResult.getChannel()) ?
                    req.getChannel() :
                    strategyResult.getChannel());
            request.setUserId(req.getUserId());
            String isAccountType = StringUtil.isBlank(req.getIsAccountType()) ?
                    ISAccountType.BASE_JIFEN.getCode() :
                    req.getIsAccountType();
            request.setIsAccountType(isAccountType);

            //版本更新，直接将版本号作为追踪ID
            if (StringUtil.equals(ISChannelType.UPDATE_VERSION.getCode(), req.getChannel())) {
                request.setOutTradeNo(extParam.get(ExtParamKeys.EXT_VERSION));
            } else {
                request.setOutTradeNo(
                        StringUtil.isBlank(outTradeNo) ? MACID.nextId().toString() : outTradeNo);
            }
            result = iSUserBehaviorRepository.addOrSubAmount(request, ISDoType.ADD.getCode());
            if (result.isSuccess()) {
                threadPool.execute(() -> {
                    integralMsgBusinessSelector.doHandler(request);
                });
                return ResultBuilder.succ(ISUserBehaviorResult.class);
            }
        } else {
            result = new TransactionResult();
            result.setErrorCode(ISBaseErrors.NON_CONFORMITY.getErrcode());
            result.setErrorMsg(ISBaseErrors.NON_CONFORMITY.getErrmsg());
        }
        return ResultBuilder
                .fail(ISUserBehaviorResult.class, result.getErrorCode(), result.getErrorMsg());
    }

    @Override public DuibaLoginResult redirectLoginUrl(DuibaLoginQueryReq request) {
        DuibaLoginResult result = ResultBuilder.succ(DuibaLoginResult.class);
        ConditionValidator.validate(request);
        String userId = request.getUserId();
        DuibaLoginReq loginReq = new DuibaLoginReq();
        if (StringUtil.isNotEmpty(userId)) {
            loginReq.setUid(userId);
            long credits = queryUserCredits(userId);
            loginReq.setCredits(credits);
            loginReq.setRedirect(request.getRedirect());
            loginReq.setDcustom(request.getDcustom());
            result = iSUserBehaviorRepository.redirectLoginUrl(loginReq, result);
            return result;
        } else {
            result = new DuibaLoginResult();
            result.setErrcode(ISBaseErrors.NO_PARAMETER.getErrcode());
            result.setErrmsg(ISBaseErrors.NO_PARAMETER.getErrmsg());
        }
        return result;
    }

    @Override public DuibaResult duibaAdd(DuibaReq duibaRequest) {
        ConditionValidator.validate(duibaRequest);
        logger.info("duibaAdd request:{}", duibaRequest.toString());
        Map map = duibaRequest.getMap();
        DuibaAddReq request = ClassFieldsUtil.map2Obj(map, DuibaAddReq.class);
        DuibaResult result = checkRequest(request.getAppKey(), request.getTimestamp(), map,
                request.getUid());
        boolean lockFlag = false;
        try {
            if (StringUtil.isEmpty(result.getErrorMessage())) {

                Long setResult = RedisUtil.setnx(jedisPool, LOCK_USER_KEY + request.getUid(), "");
                if (Objects.nonNull(setResult) && setResult.intValue() == 1) {
                    RedisUtil.expire(jedisPool, LOCK_USER_KEY + request.getUid(), 10);
                    lockFlag = true;
                } else {
                    logger.info("redis锁失败,系统异常,setResult:{}", setResult);
                    result.setErrorMessage("重复订单");
                    result.setStatus("fail");
                    return result;
                }

                List<ISAccountDetail> isAccountDetail = queryByOutNum(request.getUid(),
                        request.getOrderNum(), ISDoType.ADD.getCode());
                if (CollectionUtils.isEmpty(isAccountDetail)) {
                    ISUserBehaviorReq req = new ISUserBehaviorReq();
                    req.setUserId(request.getUid());
                    req.setIsAccountType(ISAccountType.DUIBA_JIFEN.getCode());
                    req.setIsChannelType(request.getType().toUpperCase());
                    req.setAmount(Long.parseLong(request.getCredits()));
                    req.setOutTradeNo(request.getOrderNum());
                    TransactionResult transactionResult = iSUserBehaviorRepository
                            .addOrSubAmount(req, ISDoType.ADD.getCode());
                    String s = DateUtils.formatLongFormat(new Date());
                    if (transactionResult.isSuccess()) {
                        result.setStatus("ok");
                        result.setBizId(s + RandomCharset.randomNumber(8));
                        long credits = queryUserCredits(request.getUid());
                        result.setCredits(credits);
                    }
                    //调用微管家发送签到提醒,当类型为签到时
                    logger.info("兑吧推送，request = {}",request);
                    if (DuibaType.SIGN.getCode().toLowerCase().equals(request.getType().toLowerCase())) {
                        threadPool.execute(()->{
                            UserInfoQueryRequest userInfoQueryRequest = new UserInfoQueryRequest();
                            userInfoQueryRequest.setMasterUserId(request.getUid());
                            SimpleResult<List<UserInfoDTO>> simpleResult = userInfoService.queryUserInfos(userInfoQueryRequest);
                            logger.info("请求用户信息，result={}",simpleResult);
                            List<UserInfoDTO> data = simpleResult.getData();
                            if(!CollectionUtils.isEmpty(data)){
                            //调用微管家
                                List<BaseUser> user = new LinkedList<>();
                                for (UserInfoDTO dto : data) {
                                    BaseUser baseUser = new BaseUser(dto.getAppId(),dto.getOpenid());
                                    user.add(baseUser);
                                }
                                SendTemplateMsgRequest apps = new SendTemplateMsgRequest();
                                apps.setUser(user);
                                signSuccessService.sendTemplateMsg(apps);
                            }else {
                                logger.info("签到提醒，未查询到用户信息！masterUserId={}",request.getUid());
                            }
                        });
                    }

                } else {
                    long credits = queryUserCredits(request.getUid());
                    result.setCredits(credits);
                    result.setErrorMessage("重复订单");
                    result.setStatus("fail");
                }
            }
            logger.info("duibaAdd result:{}", result.toString());

        } catch (Exception e) {
            logger.error("duibaAdd error", e);
        } finally {
            if (lockFlag) {
                RedisUtil.del(jedisPool, LOCK_USER_KEY + request.getUid());
            }
        }
        return result;
    }

    @Override public DuibaResult duibaSub(DuibaReq duibaRequest) {
        ConditionValidator.validate(duibaRequest);
        logger.info("duibaSub request:{}", duibaRequest.toString());
        Map map = duibaRequest.getMap();
        DuibaSubReq request = ClassFieldsUtil.map2Obj(map, DuibaSubReq.class);
        DuibaResult result = checkRequest(request.getAppKey(), request.getTimestamp(), map,
                request.getUid());
        if (StringUtil.isEmpty(result.getErrorMessage())) {
            List<ISAccountDetail> isAccountDetail = queryByOutNum(request.getUid(),
                    request.getOrderNum(), ISDoType.SUB.getCode());
            if (CollectionUtils.isEmpty(isAccountDetail)) {
                String s = DateUtils.formatLongFormat(new Date());
                String bizId = s + RandomCharset.randomNumber(8);
                ISUserBehaviorReq req = new ISUserBehaviorReq();
                req.setUserId(request.getUid());
                req.setIsAccountType(ISAccountType.DUIBA_JIFEN.getCode());
                req.setIsChannelType(request.getType().toUpperCase());
                req.setAmount(Long.parseLong(request.getCredits()));
                req.setOutTradeNo(request.getOrderNum());
                req.setMemo(bizId);
                TransactionResult transactionResult = iSUserBehaviorRepository
                        .addOrSubAmount(req, ISDoType.SUB.getCode());

                if (transactionResult.isSuccess()) {
                    result.setStatus("ok");
                    result.setBizId(bizId);
                    long credits = queryUserCredits(request.getUid());
                    result.setCredits(credits);
                }
            } else {
                long credits = queryUserCredits(request.getUid());
                result.setCredits(credits);
                result.setErrorMessage("重复订单");
                result.setStatus("fail");
            }
        }

        logger.info("duibaSub result:{}", result.toString());
        return result;
    }

    @Override public String duibaExchange(DuibaReq duibaRequest) {
        String duibaResult = "fail";
        ConditionValidator.validate(duibaRequest);
        logger.info("duibaExchange request:{}", duibaRequest.toString());
        Map map = duibaRequest.getMap();
        DuibaExchangeReq request = ClassFieldsUtil.map2Obj(map, DuibaExchangeReq.class);
        DuibaResult result = checkRequest(request.getAppKey(), request.getTimestamp(), map,
                request.getUid());
        boolean lockFlag = false;
        try {
            if (StringUtil.isEmpty(result.getErrorMessage())) {
                Long setResult = RedisUtil.setnx(jedisPool, LOCK_USER_KEY + request.getUid(), "");
                if (Objects.nonNull(setResult) && setResult.intValue() == 1) {
                    RedisUtil.expire(jedisPool, LOCK_USER_KEY + request.getUid(), 10);
                    lockFlag = true;
                } else {
                    logger.info("redis锁失败,系统异常,setResult:{}", setResult);
                    return duibaResult;
                }
                String orderNum = request.getOrderNum();
                String success = request.getSuccess();
                List<ISAccountDetail> isAccountDetail = queryByOutNum(request.getUid(),
                        request.getOrderNum(), null);
                if (!CollectionUtils.isEmpty(isAccountDetail)) {
                    if (isAccountDetail.size() <= 1) {
                        ISAccountDetail detail = isAccountDetail.get(0);
                        if (detail.getOpType().equals(ISDoType.SUB.getCode())) {
                            if (StringUtil.equals(success, "true")) {
                                duibaResult = "ok";
                            } else {
                                //失败
                                ISUserBehaviorReq req = new ISUserBehaviorReq();
                                req.setUserId(request.getUid());
                                req.setIsAccountType(ISAccountType.DUIBA_JIFEN.getCode());
                                req.setIsChannelType(detail.getChannel().toUpperCase());
                                req.setAmount(detail.getAmount());
                                req.setOutTradeNo(request.getOrderNum());
                                TransactionResult transactionResult = iSUserBehaviorRepository
                                        .addOrSubAmount(req, ISDoType.ADD.getCode());
                                if (transactionResult.isSuccess()) {
                                    duibaResult = "ok";
                                }

                            }
                        }
                    }
                }

            }
        } catch (Exception e) {
            logger.error("duibaAdd error", e);
        } finally {
            if (lockFlag) {
                RedisUtil.del(jedisPool, LOCK_USER_KEY + request.getUid());
            }
        }
        return duibaResult;

    }

    public DuibaResult checkRequest(String appKey, String timestamp,
                                    Map<String, String> stringObjectMap, String userId) {

        String duibaAppKey = appConstConfig.getDuibaAppKey();
        DuibaResult result = new DuibaResult();

        if (!duibaAppKey.equals(appKey)) {
            result.setErrorMessage("appKey不匹配");
        }
        if (timestamp == null) {
            result.setErrorMessage("请求中没有带时间戳");
        }
        if (userId == null) {
            result.setErrorMessage("用户信息为空");
        }

        boolean verify = SignTool.signVerify(appConstConfig.getDuibaAppSecret(), stringObjectMap);
        if (!verify) {
            result.setErrorMessage("签名验证失败");
        }
        if (StringUtil.isNotEmpty(result.getErrorMessage())) {
            result.setStatus("fail");
        }
        return result;
    }

    public long queryUserCredits(String userId) {
        ISAccountInfoQueryReq queryReq = new ISAccountInfoQueryReq();
        queryReq.setUserId(userId);
        queryReq.setIsAccountType(ISAccountType.DUIBA_JIFEN.getCode());
        ISAccountInfoDTO isAccountInfoDTO = iSBaseQueryRepository.queryISAccountInfo(queryReq);
        long credits = 0;
        if (isAccountInfoDTO != null && isAccountInfoDTO.getAmount() != null) {
            credits = isAccountInfoDTO.getAmount();
        }
        return credits;
    }

    public List<ISAccountDetail> queryByOutNum(String userId, String outNum, String opType) {
        ISAccountDetailExample example = new ISAccountDetailExample();
        ISAccountDetailExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(userId);
        if (StringUtil.isNotEmpty(opType)) {
            criteria.andOpTypeEqualTo(opType);
        }
        criteria.andOutTradeNoEqualTo(outNum);

        List<ISAccountDetail> wxSendTemplateLogs = iSAccountDetailMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(wxSendTemplateLogs)) {
            return null;
        } else {
            return wxSendTemplateLogs;
        }
    }

}
